#include "main.h"
#include "common.h"
#include "log_print.h"
#include "database.h"
#include "mqtt_client.h"
#include "zlog.h"

#include <fcntl.h>
#include <execinfo.h>
#include <unwind.h>
#include <libunwind-aarch64.h>

#ifdef __USE_GNU
#include <dlfcn.h>
#else
#include "aarch64_dlfcn.h"
#endif

// 定义一个回调函数，用于处理每个栈帧的信息
static _Unwind_Reason_Code unwind_callback(struct _Unwind_Context *context, void *arg)
{
    char func_name[256];
    Dl_info dlinfo;
    unw_word_t ip = _Unwind_GetIP(context);

    if (dladdr((void *)ip, &dlinfo) && dlinfo.dli_sname)
    {
        snprintf(func_name, sizeof(func_name), "%s+%lu",
                 dlinfo.dli_sname, ip - (uintptr_t)dlinfo.dli_saddr);
    }
    else
    {
        snprintf(func_name, sizeof(func_name), "%lx", (unsigned long)ip);
    }

    LOG_PRINTF_ERROR( "func_name[%s], len: %d", func_name, strlen(func_name));
    if (dlinfo.dli_fname)
    {
        LOG_PRINTF_ERROR( "(%s)", dlinfo.dli_fname);
    }
    else
    {
        LOG_PRINTF_ERROR( "?");
    }

    return _URC_NO_REASON;
}

static void print_stack_trace()
{
    // 使用libunwind获取更详细的堆栈信息
    _Unwind_Backtrace(unwind_callback, NULL);
}

static void signal_handler(int sig)
{
    switch (sig) {
        case SIGINT:  /* ctrl + c */
        case SIGILL:
        case SIGBUS:
        case SIGSEGV:
        case SIGFPE:
        case SIGABRT:
        case SIGSTKFLT:
        case SIGUSR2:
        case SIGTERM:
        case SIGSYS:
            LOG_PRINTF_ERROR( "Received signal %d", sig);
            print_stack_trace();

            set_app_running(0);
            // log_manage_destroy();
            database_destory();
            mqtt_client_destory();
            LOG_PRINTF_ERROR( "egw_basic app stopped.");
            log_destory();
            exit(0);
            break;

        case SIGPIPE:
            break;

        default:
            break;
    }
}

static void help(void)
{
    printf("Usage:\n");
    printf("option:\n");
    printf("       -h print help information\n");
    printf("       -v print softwear version\n");
    printf("       -DV log out with verbose\n");
    printf("       -DI log out with information\n");
    printf("       -DD log out with debug\n");
    printf("       -DW log out with warnning\n");
    printf("       -DE log out with error\n");
}

int main(int argc, char **argv)
{
    int ch;
    char opc_url[50];
    int rtn;

    /* configure signal handling */
    signal(SIGPIPE, signal_handler);
    signal(SIGINT, signal_handler);

    /* 用于程序异常退出时，捕获堆栈信息 */
    signal(SIGILL, signal_handler);
    signal(SIGBUS, signal_handler);
    signal(SIGSEGV, signal_handler);
    signal(SIGFPE, signal_handler);
    signal(SIGABRT, signal_handler);
    signal(SIGSTKFLT, signal_handler);
    signal(SIGUSR2, signal_handler);
    signal(SIGTERM, signal_handler);
    signal(SIGSYS, signal_handler);

    /* parse command line options */
    while ((ch = getopt(argc, argv, "hvD:O:")) != -1) {
        switch (ch) {
            case 'h':
                help();
                return 0;
                break;

            case 'v':
                printf("%s %s %s\n", SOFTWARE_VERSION, get_compiled_date_yyyy_mm_dd(), __TIME__);
                return 0;

            case 'D':
                if (!strcmp(optarg, "V")){}
                else if (!strcmp(optarg, "I")){}
                else if (!strcmp(optarg, "D")){}
                else if (!strcmp(optarg, "W")){}
                else if (!strcmp(optarg, "E")){}
                else{}

                break;

            case 'O':
                strcpy(opc_url, optarg);
                break;

            default:
                help();
                return 0;
        }
    }

    /* zlog初始化 */
    log_init();

    /* set app running */
    set_app_running(1);

    rtn = read_json_config();
    if (0 != rtn) {
        LOG_PRINTF_ERROR( "read_json_config err[%d]", rtn);
    }

    log_printf(MSG_INFO, "APP[%s] %s start, compile time: %s %s", APP_NAME, SOFTWARE_VERSION, __DATE__, __TIME__);

    database_init();

    mqtt_client_init();

    pause();

    log_destory();

    return 0;
}